Vim 脚本简介

Vim 简介

Vim提供两种方式进行脚本编程:其一是使用内置的脚本语言,另一种是使用全功能的外置语言,比如Python或是Perl。

在Vim中有两种方式生成可重用的函数:使用宏,还有就是编写脚本。

注意 如果你仅仅想重复你最后一次动作的话,不需要用到宏就可以,仅仅需要按一下.(点号) 基本脚本编程 VIM已经内置了脚本语言的支持,通过这个功能你可以写自己的脚本来做决定、完成某些事情或是维护文本。

开始行动 怎么才能更改主题呢?比如说,Vim中使用的颜色,你只需要运行:

:colorscheme desert 这里我使用的是‘desert’颜色模式,这可是我最喜欢的颜色模式:P。你可以通过敲入下面的命令来查看其他可用的颜色模式: :colorscheme 同时可以通过<tab>来浏览其他可用的模式。 要是你想知道当前这一行到底有多少字符呢?

:echo strlen(getline(".")) 注意:这里的‘strlen’ 和‘getline’都是函数名称。所谓函数就是一段已经写好的代码,你可以通过他们的名字多次引用。例如:getline函数读取一行,同时我们通过 . (点号)来指示当前的行。之后我们把getline的结果作为strlen函数的输入,通过他来计算行中的字数,然后把结果再次传递给:echo命令,这条命令仅仅是显示结果。请注意这条命令中函数的顺序。

其中 strlen(getline(".")) 叫做一条表达式。我们可以通过变量来存储一条表达式的结果。变量,正如他的名字所显示的,是一个指向内容(values)的名字,所指向的内容可以是任何内容。比如你可以将长度值保存成这样子:

:let len = strlen(getline(".")) :echo "We have" len "characters in this line." 当你执行上述命令之后,你会得到如下输出:

We have 46 characters in this line. 请注意我们是怎么在表达式中使用变量的。通过使用这些变量、表达式和命令你可以完成的工作是无限的(endless)。

Vim根据前缀定义了一系列变量类型,包括:$代表环境变量,&代表选项,@代表寄存器:

:echo $HOME :echo &filetype :echo @a 使用:help function-list命令 来查看更多可用的函数。

当然你也可以建立自己的函数:

:function CurrentLineLength() : let len = strlen(getline(".")) : return len :endfunction 现在将你的光标定位到任意一行,然后输入下边命令:

:echo CurrentLineLength()

你将会看到输出结果。

为了区别内置的函数和你自己定义的函数,你自己定义的函数名需要以大写字母开头。如果你仅仅想调用一个函数而不是显示内容的话,你可以使用 :call CurrentLineLength()。

条件判别 假如你想让根据你的Vim编辑器是运行在终端模式还是GUI模式来决定不同的显示模式,比如说:你需要脚本来作决定的话,你需要这样:

:if has("gui_running") : colorscheme desert :else : colorscheme darkblue :endif 上述命令是如何工作的:

has() 函数是用来检查某些特定的功能是否已经在当前的Vim中安装了。可以通过 :help feature-list 查看特性的种类。 The if s语句是用来检查所提供的条件是否成立。如果if条件成立,我们执行当前命令。“ELSE”我们执行另一个命令。 注意 if 语句需要一个 endif 结束。 elseif 同样是支持的,适用于多条件选择。 循环指令,包括'for'和'while'在Vim中同样是支持的:

:let i = 0 :while i < 5 : echo i : let i += 1 :endwhile 输出如下:

0 1 2 3 4 通过使用Vim的内置函数,上述语句可以写成这样:

:for i in range(5) : echo i :endfor range() 是一个内置函数,用来生成一定范围的数字。通过 :help range() 来查看更多内容。 同样支持 continue 和 break 语句。 数据结构 Vim脚本语言同样支持lists和dictionaries。 通过使用这些你可以建立复杂的数据结构和程序。

:let fruits = ['apple', 'mango', 'coconut']
 
:echo fruits[0]
" apple
 
:echo len(fruits)
" 3
 
:call remove(fruits, 0)
:echo fruits
" ['mango', 'coconut']
 
:call sort(fruits)
:echo fruits
" ['coconut', 'mango']
 
:for fruit in fruits
:   echo "I like" fruit
:endfor
" I like coconut
" I like mango

同样有好多函数可以使用,运行 :help function-list ,查看 'List manipulation' 和 'Dictionary manipulation' 章节以获得更多信息。

开始编写Vim脚本 接下来我们开始写能够加载进Vim的脚本,这样我们就可以在任何需要的时候调用了。这和之前我们写的那种内嵌的并且立即执行的脚本有些不同。

我们先从处理简单问题开始:如何将选定的行的首字母全部大写?这种情况是很常见的,当我写文章的时候,将段落开头的单词首字母大写可以使文章更漂亮一些。但是我很懒,懒得去自己做这件事==!所以我打算直接小写文章,然后调用脚本函数,让他来帮我干这件事。

我们从一个简单的模板开始。将这个文件保存为capitalize.vim

" Vim global plugin for capitalizing first letter of each word
"       in the current line.
" Last Change: 2008-11-21 Fri 08:23 AM IST
" Maintainer: www.swaroopch.com/contact/
" License: www.opensource.org/licenses/bsd-license.php
 
if exists("loaded_capitalize")
    finish
endif
let loaded_capitalize = 1

" TODO : The real functionality goes in here.

上述命令是如何工作的:

第一行是一行注释,用来解释这个脚本是干什么的。 之后两行是标准的头部信息。'Last Changed'说明程序的最后修改时间;'Maintainer'显示了脚本的维护者信息,有什么问题可以和这个人联系,当然你也可以通过这个信息来表示感谢。 'License'是可以选的,但是我要强烈推荐使用。或许你写的这个Vim脚本或是插件对其他人很有用,这样你就需要为你的脚本声明一个许可。这样别人就可以改进你的脚本,反过来同样对你也有利。 一个脚本可能会载入多次。比如说你在一个Vim例程中打开两个不同的文件,他们都是 .html 文件,这时Vim就会打开用于HTML语法高亮脚本同时运用于这两个文件。如果你想要避免同时运行一个脚本多次,或是重定义某些东西多次,我们用了一条语句来检查'loaded_capitalize'是否存在,同时如果已经被加载的话,再次加载的将会被关闭。 OK,我们现在继续完善脚本的实际功能。

我们可以定义一个函数来完成这个单词首字母大小写转换的功能,我们就将这个函数定义为 Capitalize()。既然这个函数是按顺序执行的,我们就按顺序的方式编写它。

" Vim global plugin for capitalizing first letter of each word
"       in the current line
" Last Change: 2008-11-21 Fri 08:23 AM IST
" Maintainer: www.swaroopch.com/contact/
" License: www.opensource.org/licenses/bsd-license.php

" Make sure we run only once
if exists("loaded_capitalize")
    finish
endif
let loaded_capitalize = 1

" Capitalize the first letter of each word
function Capitalize() range
    for line_number in range(a:firstline, a:lastline)
        let line_content = getline(line_number)
        " Luckily, the Vim manual had the solution already!
        " Refer ":help s%" and see 'Examples' section
        let line_content = substitute(line_content, "\\w\\+", "\\u\\0", "g")
        call setline(line_number, line_content)
    endfor
endfunction

上述命令是如何工作的:

a:firstline 和 a:lastline 声明分别代表传递进来的一行文字的开头结尾。 我们使用了一个for循环来处理使用 (getline()) 取回的每一行内容。 使用 substitute() 函数来执行一个正则表达式的字符串查找和替换。 这里的意思通过 '\\w\\+' 模式来查找一个单词(比如: 组成单词的连续的一串字符串)。 一旦找到模式匹配的单词,程序将会通过 \\u\\0 模式将它们转换成大写,其中 \\u 代表将后面跟着的序列的第一个字母转换成大写, \\0 代表substitute()函数找到的内容,也就是这个单词。事实上我们将每一个单词的第一个字母都转换成了大写了。 最后我们调用 setline() f函数来将原内容替换成修改好的内容。 如何执行这条命令:

首先打开Vim并随便输入一些单词,比如:this is a test。 执行 :source capitalize.vim - 'sources'使得我们写的脚本能想之前的内置函数一样使用。 执行 :call Capitalize()。 现在这一行文本将会变成'This Is A Test'。 但是每次都是输入 :call Capitalize() 是很乏味的,所以我们可以通过<leader>来设置快捷键:

" Vim global plugin for capitalizing first letter of each word
" in the current line
" Last Change: 2008-11-21 Fri 08:23 AM IST
" Maintainer: www.swaroopch.com/contact/
" License: www.opensource.org/licenses/bsd-license.php

" Make sure we run only once
if exists("loaded_capitalize")
    finish
endif
let loaded_capitalize = 1

" Refer ':help using-<Plug>'
if !hasmapto('<Plug>Capitalize')
    map <unique> <Leader>c <Plug>Capitalize
endif
noremap <unique> <script> <Plug>Capitalize <SID>Capitalize
noremap <SID>Capitalize :call <SID>Capitalize()<CR>

" Capitalize the first letter of each word
function s:Capitalize() range
    for line_number in range(a:firstline, a:lastline)
        let line_content = getline(line_number)
        " Luckily, the Vim manual had the solution already!
        " Refer ":help s%" and see 'Examples' section
        let line_content = substitute(line_content, "\\w\\+", "\\u\\0", "g")
        call setline(line_number, line_content)
    endfor
endfunction

我们将函数的名字从简单的Capitalize改成了s:Capitalize,代表了这个函数是在脚本里定义的,同时避免全局可见,因为我们可不想让他与其他同名的脚本函数产生冲突。 我们使用 map 命令来定义快捷键。 <Leader> 键一般就是反斜杠'\'(blackslash)。 我们现在定义<leader>c(意思是leader键紧跟着c)作为快捷键。 我们使用 <Plug>Capitalize 来指明 Capitalize() 函数来自一个插件或是说我们的脚本。 每个脚本都有一个ID号,通过 <SID>来指定。 所以我们通过使用命令 <SID>Capitalize 来映射一个本地函数 Capitalize()。 现在重复之前提到的步骤来测试这个脚本,只是你现在可以使用 \\c 来代替 :call Capitalize()。

最后一系列步骤看起来有些复杂,但是我只是想让你知道有很多的方法来生成你自己的脚本,并且通过他们你能做好多复杂的工作。

如果你的脚本出了什么错误,你可以使用 v:errmsg 来查看最后的错误消息,这里可以给你一个查看到底是什么出错的线索。

注意:你可以使用 :help 来查看你想知道的任何帮助信息。还有 :help \\w 和 :help setline()。

使用外部编程语言 可能很多人并不喜欢花很多时间来学习Vim的脚本语法或是更喜欢使用他们已知的语言来写Vim的插件。这当然也是可行的,因为Vim支持使用Python,Perl,Ruby等一些其他语言来写插件。

这一小节里,我们将会看一个用Python写的简单的插件,当然我们也可以使用其他语言来完成。

首先我们要测试一下当前环境是否支持Python:

:echo has("python") 如果返回1的话,代表环境支持,否则的话你就需要安装Python环境,然后再试一次了。

假如你有一个自己的Blog,每一个Blog作者都希望尽可能多的人来阅读他的blog。人们找到这个blog 的其中一个途径就是通过搜索引擎。所以当你打算根据某一话题写一篇文章的话,你可能希望使用一些关键词来帮助那些正在搜索这个话题的读者来找到这篇文章。比如说当人们搜索“C V Raman”的同时,他们也可能会搜索“Raman effect”。

那么怎么才能找到这些相关的词语呢? 感谢yahoo!搜索,他将这个问题变得很简单:P

首先我们看一下怎么使用Python来访问当前的文本,通过这个我们来生成相关短语。

" Vim plugin for looking up popular search queries related
"       to the current line
" Last Updated: 2008-11-21 Fri 08:36 AM IST
" Maintainer: www.swaroopch.com/contact/
" License: www.opensource.org/licenses/bsd-license.php

" Make sure we run only once
if exists("loaded_related")
    finish
endif
let loaded_related = 1

" Look up Yahoo Search and show results to the user
function Related()
python <<EOF
 
import vim
 
print 'Length of the current line is', len(vim.current.line)
 
EOF
endfunction

用提供接口的语言来写插件的最主要的方法和内置的脚本语言是一样的。

最关键的不同点就是我们必须将这些代码传递给Python解释器。 这是通过上方的EOF标记来实现的 - 从python <<EOF 开始到 EOF 的所有文本都会传递给Python解释器。

要测试这个脚本的话,你需要单独打开一个Vim,执行 :source related.vim,然后执行 :call Related(). 他将会输出类似这样的结果 Length of the current line is 54。

OK,现在我们深入了解一下这个函数。 Yahoo! Search有个功能叫做 RelatedSuggestion query 我们可以联网通过yahoo! 提供的Python API来使用他。pYsearch:

" Vim plugin for looking up popular search queries related
" to the current line
" Last Updated: 2008-11-21 Fri 08:36 AM IST
" Maintainer: www.swaroopch.com/contact/
" License: www.opensource.org/licenses/bsd-license.php

" Make sure we run only once
if exists("loaded_related")
    finish
endif
let loaded_related = 1

" Look up Yahoo Search and show results to the user
function Related()
python <<EOF
 
import vim
from yahoo.search.web import RelatedSuggestion
 
search = RelatedSuggestion(app_id='vimsearch', query=vim.current.line)
results = search.parse_results()
 
msg = 'Related popular searches are:\n'
i = 1
for result in results:
    msg += '%d. %s\n' % (i, result)
    i += 1
print msg
 
EOF
endfunction

请注意,我们使用Vim的当前行作为我们感兴趣的那行,当然你也改成任何你喜欢的文本或是单词等。

我们使用yahoo.search.web.RelatedSuggestion 类来进行相关查询。通过调用 parse_results() 来获得返回的结果。接着我们循环显示这些结果。

执行 :source related.vim 输入文本 c v raman. 执行 :call Related() 输出应该类似于这样: Related popular searches are: 1. raman effect 2. c v raman india 3. raman research institute 4. chandrasekhara venkata raman 总结 我们已经体验了使用Vim内置的脚本语言和外部脚本语言,这一点非常重要,正因为如此才使得Vim变成可以无限扩展的程序。

更多内容查看 :help eval, :help python-commands, :help perl-using 和 :help ruby-commands 。